
/*

Helper class for dealing with enumerated configuration file lists like the 
following:

<List Name="HTTPDeliverable">
    <Var Path_0="/admin"/>
    <Var Path_2="/ramgen"/>
    <Var Path_1="/farm"/>
    <Var Path_3="/scalable"/>
</List>

Usage:

in server code do something like this:

emitPropsToJSArray( "config.HTTPDeliverable", "httpDelivList", 0, 0 );

On the client side, instantiate an EnumConfigList object like so:

var httpEnumList = new EnumConfigList( httpDelivList, "config.HTTPDeliverable", "Path_" );

during the onLoad handler for your page, use this to populate a <SELECT> list:

httpEnumList.fillSelectList( theForm.HTTPLIST );

*/

function ConfigListItem ( value, enumValue, status )
{
	this.m_value = value ;
	this.m_enum = enumValue ;
	this.m_status = status ;
}

function EnumConfigList ( list, listName, listEnumPrefix )
{
	this.list = new Array();
	this.listName = listName ? listName : "" ;
	this.prefix = ( listEnumPrefix ? listEnumPrefix : "item_" );
	this.usedIndexes = new Array( 100 );

	// pivot the original list into a hash for easy lookup (eliminates duplicates also)
	var regEx = new RegExp( "^" + escRegExMetaChars( this.prefix ), "i" );
	var index = null ;
	if ( list )
	{
		for ( var key in list )
		{
			this.list[ list[ key ] ] = new ConfigListItem( list[ key ], key, null );
			// parse the index number Ex: "Path_1" -> "1"
			var index = parseInt( key.replace( regEx, "" ) );
			if ( ! isNaN( index ) )
			{
				this.usedIndexes[ index ] = true ;
			}
		}
	}

	return this ;
}

EnumConfigList.prototype.add = function ( value )
{
	if ( ( value == "" ) || ( value == null ) )
	{
		return ;
	}

	// check and see if we got one already
	if ( this.list[ value ] != null )
	{
		switch( this.list[ value ].m_status )
		{
			case "replaced" :
			{
				this.assignNextIndex( this.list[ value ] );
			}
			// fall through to "deleted"

			case "deleted" :
			{
				this.list[ value ].m_status = null ;
				return true ;
			}

			case "added" :
			default:
			{
				return true ; 
			}
		}
	}

	this.list[ value ] = new ConfigListItem( value, "", "added" );
	this.assignNextIndex( this.list[ value ] );
}

// return true if change was made, flase if not
EnumConfigList.prototype.replace = function ( oldValue, newValue )
{
	// if the oldValue didn't exist
	if ( this.list[ oldValue ] == null )
	{
		// need to add the new value ?
		if ( this.list[ newValue ] != null )
		{
			return false ;
		}
		else
		{
			if ( ( newValue != "" ) && ( newValue != null ) )
			{
				this.list[ newValue ] = new ConfigListItem( newValue, "", "added" );
				this.assignNextIndex( this.list[ newValue ] );
				return true ;
			}
			return false ;
		}
	}

	// does the new value exist already?
	if ( this.list[ newValue ] != null )
	{
		// just delete the old value
		if ( oldValue == newValue ) 
		{
			return false ;
		}
		else
		{
			this.list[ oldValue ].m_status = "deleted" ;
			return true ;
		}
	}

	this.list[ newValue ] = new ConfigListItem( newValue, this.list[ oldValue ].m_enum, "added" );
	if ( this.list[ oldValue ].m_status == "added" )
		delete this.list[ oldValue ];
	else
		this.list[ oldValue ].m_status = "replaced" ;

	return true ;	
}

EnumConfigList.prototype.remove = function ( value )
{
	// check and see if we got one already
	if ( this.list[ value ] == null )
		return true ;

	this.list[ value ].m_status = "deleted" ;
}

// convert changes into a ra param string
EnumConfigList.prototype.buildParamString = function ( fForce )
{
	var s = "" ;
	for ( var value in this.list )
	{
		var item = this.list[ value ];
		switch( item.m_status )
		{
			case "added" :
			{
				s += buildParam( this.listName + "." + item.m_enum, value );
				break ; 
			}

			case "deleted" :
			{
				s += buildParam( this.listName + "." + item.m_enum, "" );
				break ; 
			}

			case "replaced" :
			{
				break ; 
			}
			default:
			{
				if ( fForce )
				{
					s += buildParam( this.listName + "." + item.m_enum, value );
				}

				break ; 
			}
		}
	}

	return s ;
}

EnumConfigList.prototype.fillSelectList = function ( selectCtrl, fNoSort )
{
	var aTemp = new Array();
	for ( var value in this.list )
	{
		var item = this.list[ value ];
		switch( item.m_status )
		{
			case "deleted" :
			case "replaced" :
			{
				break ; 
			}

			case "added" :
			default:
			{
				aTemp[ aTemp.length ] = value ;
				break ; 
			}
		}
	}

	if ( ! fNoSort )
	{
		if ( isIE )
			aTemp.sort( sortCaseInsensitive );
		else
			aTemp.sort();	
	}
	for( var i = 0; i < aTemp.length; i++ )
	{
		selAddOption( selectCtrl, aTemp[ i ] );
	}
}

EnumConfigList.prototype.reset = function ()
{
	for ( var value in this.list )
	{
		var item = this.list[ value ];
		switch( item.m_status )
		{
			case "deleted" :
			case "replaced" :
				item.m_status = null ;
				break ;

			case "added" :
				delete this.list[ value ];
				break ;

			default:
				break ; 
		}
	}
}

EnumConfigList.prototype.clearList = function ()
{
	this.list = new Array();
	this.usedIndexes = new Array( 100 );
}

EnumConfigList.prototype.commit = function ()
{
	for ( var value in this.list )
	{
		var item = this.list[ value ];
		switch( item.m_status )
		{
			case "replaced" :
			case "deleted" :
				delete this.list[ value ];
				break ;

			case "added" :
				item.m_status = null ;
				break ;

			default:
				break ; 
		}
	}
}

EnumConfigList.prototype.assignNextIndex = function ( item )
{
	// skip 0'th element
	for( var i = 1; i < this.usedIndexes.length; i++ )
	{
		if ( ! this.usedIndexes[ i ] )
			break ;
	}

	this.usedIndexes[ i ] = true ;
	item.m_enum = this.prefix + i ;
}

EnumConfigList.prototype.toCSV = function ()
{
	// flatten list of values to comma seperated list (as string)
	var csv = "" ;
	for ( var value in this.list )
	{
		csv += value + ", " ;
	}
	return csv.replace( /, $/, '' );
}

// turn a comma seperated list ("htm,html,shtml,shtm") into an enum list
// like this: Ext_1=htm&Ext_2=html, etc.
EnumConfigList.prototype.removeCSV = function ( csv )
{
	if ( ! csv ) return ;

	var val ;
	var aTmp = csv.split( /\s*,\s*/ );
	for ( var i = 0; i < aTmp.length; i++ )
	{
		val = aTmp[ i ];
		if ( val ) val = val.trim();
		if ( val )
		{
			this.remove( val );
		}
	}

}	// removeCSV

// turn a comma seperated list ("htm,html,shtml,shtm") into an enum list
// like this: Ext_1=htm&Ext_2=html, etc.
EnumConfigList.prototype.addCSV = function ( csv )
{
	if ( ! csv ) return ;

	var val ;
	var aTmp = csv.split( /\s*,\s*/ );
	for ( var i = 0; i < aTmp.length; i++ )
	{
		val = aTmp[ i ];
		if ( val ) val = val.trim();
		if ( val )
		{
			this.add( val );
		}
	}

}	// addCSV

EnumConfigList.prototype.dump = function ()
{
	var s = "" ;
	for ( var value in this.list )
	{
		var item = this.list[ value ];
		s += value + "\n\tenum = " + item.m_enum + "\n\tstatus = " + item.m_status + "\n\n" ;
	}
	alert( s );
}


